package cn.hn.java.summer.mvc;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.context.ApplicationContext;

import cn.hn.java.summer.annotation.ParamValid;
import cn.hn.java.summer.constants.Default;
import cn.hn.java.summer.exception.BusinessException;
import cn.hn.java.summer.exception.ForwardException;
import cn.hn.java.summer.exception.LoginException;
import cn.hn.java.summer.mvc.log.ActionLogger;
import cn.hn.java.summer.utils.StringUtils;
import cn.hn.java.summer.validate.BeanValidator;




/**
 * 拦截以Controller结尾控制器的所有方法
 * 记录日志、保存错误信息
 * @author sjg
 * @version 1.0.1 2013-10-20
 *
 */
public abstract class ControllerAspectTool{		
	static Log logger=LogFactory.getLog(ControllerAspectTool.class);	
    
	/**
	 * 用around拦截controller所有action方法，进行异常处理和日志记录
	 * @param pjp
	 * @param context
	 * @return
	 * @throws Throwable
	 */
    public static Object around(ProceedingJoinPoint pjp,ApplicationContext context) throws Throwable{
    	Method mtd=getMethod(pjp);
	    try{
	    	//取到方法参数所有注解
	    	Annotation[][] anns= mtd.getParameterAnnotations();
	    	//验证输入参数
	    	Object[] args = pjp.getArgs();
	    	for(int i=0;i<args.length;i++){
	    		//忽略为空的参数
	    		if(args[i]==null){
	    			continue;
	    		}
	    		//只对有valid注解的参数进行验证
	    		for(int j=0;j<anns[i].length;j++){
	    			if(anns[i][j] instanceof ParamValid){
	    	    		//参数注解验证
	    	    		BeanValidator.valid(args[i]);
	    	    		break;
	    			}
	    		}
	    	}
	    	
	    	//调用目标对象方法
	    	Object rst=pjp.proceed();
	    	
	    	//保存返回值的键
	    
    		//取返回值类型标识
    		String resultKey=mtd.getGenericReturnType().toString();
    		resultKey=resultKey.replaceAll(".*\\.(\\w+)(<.*\\.(\\w+)>)|.*\\.(\\w+)", "$4$3$1");
    		resultKey=resultKey.substring(0, 1).toLowerCase()+resultKey.substring(1);	    		
    		//添加到请求中，待SnJsonMaper取
    		WebContext.setRequestAttribute(Default.ACTIONRESULT_IN_REQUEST_KEY, resultKey);
    		logger.debug("resultKey=>"+resultKey);
	    	
    		//记录操作日志
    		ActionLogger.log(context,mtd, pjp.getArgs(),null);
    		
	    	return rst;

	    }catch(Exception ex){
	    	
	    	//用于跳转
	    	if(ex instanceof ForwardException || ex instanceof LoginException){
	    		throw ex;
	    	}	    	
	    		
    		//取栈中异常
    		Object stackException= WebContext.getRequestAttribute(Default.ERROR_IN_REQUEST_KEY);
    		
    		if(stackException==null){
	    		//保存异常
	    		Throwable exThrow=ex.getCause();
	    		//如果Cause为空则输出最终的包装错误信息，否则输出包装信息和原始信息
	    		String msg=exThrow==null?ex.getMessage():(ex.getMessage()+":"+exThrow.getMessage());
	    		WebContext.setRequestAttribute(Default.ERROR_IN_REQUEST_KEY, msg==null?"null":msg);    		
	    			    		
	    		//取目标方法
	    		Method mth=getMethod(pjp);
	    		String errorMsg=StringUtils.format("在 {0}.{1}: ",pjp.getTarget().getClass(), mth.getName());
	    		//非业务异常
	    		if(!(ex instanceof BusinessException)){
			    	logger.error("执行方法出错"+errorMsg,ex);	
	    		}else{
	    			//业务异常信息
	    			logger.warn("业务异常"+errorMsg+ex.getMessage());
	    		}
	    		
	    		//记录操作日志
	    		ActionLogger.log(context, mtd, pjp.getArgs(),ex.getMessage());
    		}

    		
	    	return null;
	    }
    }
    
    
    /**
     * 取方法的注解
     * @param pjp
     * @param cls
     * @return
     * @throws SecurityException
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    private static Method getMethod(ProceedingJoinPoint pjp) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{

		//获取调用方法信息
		Field methodInvocationField = pjp.getClass().getDeclaredField("methodInvocation");
		//反射获取被调用的方法信息
		methodInvocationField.setAccessible(true);
		ProxyMethodInvocation method= (ProxyMethodInvocation)(methodInvocationField.get(pjp));
		Method targetMethod=method.getMethod();
		
		return targetMethod;
    }


}
